#ifndef _FileArchive_H
#define _FileArchive_H
#include <vector>
#include <assert.h>

#define FILEARCHIVE_SIGNATURE       0x6B636170
#define FILEARCHIVE_CURRENT_VERSION 1
#define FILEARCHIVE_MIN_VERSION     1
#define FILEARCHIVE_BLOCK_SIZE      4096

typedef unsigned long index;

class CFileArchive;


struct CFileInfo{
	CFileInfo(){
		memset(this, 0, sizeof(CFileInfo));
	}
	CFileInfo(const CFileInfo& Info){
		memmove(this, &Info, sizeof(CFileInfo));
	}
	index _FirstBlock;
	union{
		index _BlockCount;
		index _NextBlock;
	};
// 	size_t _Size;
// 	unsigned long _TimeCreated, _TimeModified;
	char _Path[255];
};


struct CBlock{
	CBlock(){
		memset(this, 0, sizeof(CBlock));
	}
	index _Next, _End;
	char _Data[FILEARCHIVE_BLOCK_SIZE - sizeof(index)* 2];
};

class CFileHandler{
public:
	size_t write(const void* SrcBuf, unsigned long Size);
	size_t read(void* DstBuf, unsigned long Size);
	size_t size();
	size_t setPos(unsigned long Position);
	void   setEOF();
	void   close();
	// protected:
	// friend CBlockManager;
	CFileHandler(CFileArchive* Archive, index StartIndex, CFileInfo* FileInfo);
private:
	index _CurrentPos;
	CBlock _Buffer;
	CFileArchive* _FileArchive;
	std::vector<index> _IndexBuffer;
	size_t _Index;
	bool _IsBuffered;
	CFileInfo* _FileInfo;
};




class CFileInfoHandler : public CFileHandler{
private:
	struct CFileInfoHeader{
		CFileInfoHeader() : _TotalCount(0), _FreeIndex(0){}
		index _TotalCount;
		index _FreeIndex;
	} _Header;
	std::vector<CFileInfo> _FileInfoBuffer;


protected:
	friend CFileArchive;
	CFileInfoHandler(CFileArchive* Archive);

	bool add(const char* Path, index BlockIndex, index& Index);
	bool get(const char* Path, index& Index);

	inline CFileInfo& operator [](index Index){
		assert(!(Index >= _Header._TotalCount));
		return _FileInfoBuffer[Index];
	}

	bool  remove(const char* _Path);
	void close(){
		CFileHandler::close();
	}
};

struct CFileArchiveHeader{
	CFileArchiveHeader() :
	_Signature(FILEARCHIVE_SIGNATURE),
	_Version(FILEARCHIVE_CURRENT_VERSION),
	_TotalCount(1),
	_UsedCount(1),
	_FreeBlockIndex(1){}
	unsigned long  _Signature; // identifikacija
	index _Version;            // verzija
	index _TotalCount;         // broj blokova
	index _UsedCount;          // broj zauzetih blokova
	index _FreeBlockIndex;     // index slobodnog bloka
};

class CFileArchive : private CFileArchiveHeader{
public:
	static CFileArchive* openArchive(const char    *ArchivePath, bool createIfNoExist);
	static CFileArchive* openArchive(const wchar_t *ArchivePath, bool createIfNoExist);
	static CFileArchive* openArchive(const std::wstring& ArchivePath, bool createIfNoExist);
	void   close();

	CFileHandler* open(const char* FilePath, bool createIfNoExist);
	const std::vector<CFileInfo>& getFileInfoBuffer();
	bool   deleteFile(const char* FilePath);
	bool   exist(const char* FilePath);

protected:
	friend CFileHandler;
	friend CFileInfoHandler;

	index read(CBlock* Buffer, index BlockIndex);
	index write(CBlock* Buffer, index BlockIndex, bool GetNext = false);
	index getBlocks(std::vector<index>& IndexBuffer, index BlockCount);
	void  saveHeader(index FreeBlock);
	index newBlock(CBlock* Buffer);
	void  free(index BlockIndex);

private:
	CFileArchive(FILE* Stream, const wchar_t* ArchivePath, bool& IsOk);
	~CFileArchive();
	void seekToStart();
	void seek(index BlockIndex);
	inline size_t blockOffset(index BlockIndex){
		return(sizeof(CFileArchiveHeader)+sizeof(CBlock)* BlockIndex);
	}


	std::vector<CFileHandler*> _OpenedFiles;
	FILE* _Stream;
	const std::wstring _ArchivePath;
	static std::vector<CFileArchive*> _Archives;
	CFileInfoHandler* _FileInfoHandler;
	size_t _LockedCount;
};


#endif /* _FileArchive_H */